/* 
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 3 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <QRegExp>
#include <QMap>
#include <QStringList>
#include <QtXml>

#include "timestampparser.h"
#include "log.h"
#include "hanabiraparser.h"
#include "text.h"

namespace Wishmaster
{
	class HanabiraParserImpl
	{
	public:
		HanabiraParserImpl(const QString& boardname);
		QString nakedBoardName();
		QString processPostText(const QString& posttext, const ThreadId& threadId);
		QString m_boardname;
		QRegExp m_bold;
		QRegExp m_italic;
		TimestampParser m_timestampParser;
	};

	HanabiraParserImpl::HanabiraParserImpl(const QString &boardname) : m_boardname(boardname)
	{
		m_bold = QRegExp("\\*([^*]*)\\*");
		m_bold.setMinimal(true);

		m_italic = QRegExp("\\*\\*([^*]*)\\*\\*");
		m_italic.setMinimal(true);
	}

	HanabiraParser::HanabiraParser(const QString& boardname) : BoardParser()
	{
		m_impl = new HanabiraParserImpl(boardname);
	}

	HanabiraParser::~HanabiraParser()
	{
		delete m_impl;
	}

	QString HanabiraParserImpl::nakedBoardName()
	{
		QString bn = m_boardname;
		if(bn.startsWith('/'))
			bn = bn.mid(1);
		if(bn.endsWith('/'))
			bn.chop(1);
		return bn;
	}

	QList<ThreadInfo> HanabiraParser::getThreadList(const QString& chanName, const QString& boardId, const QByteArray& input)
	{
		QList<ThreadInfo> list;
		QRegExp dateTimeRx("(\\d+)-(\\d+)-(\\d+) (\\d+):(\\d+):(\\d+)");
		QDomDocument doc;
		doc.setContent(input);
		QDomNodeList threads = doc.firstChildElement("result").firstChildElement("boards").firstChildElement(m_impl->nakedBoardName()).elementsByTagName("threads");
		for(int i = 0; i < threads.count(); i++)
		{
			QDomElement thread = threads.at(i).toElement();
			ThreadInfo info;
			info.thread_id = ThreadId(chanName, boardId, thread.firstChildElement("display_id").text().toUInt());
			info.topic = thread.firstChildElement("title").text();
			info.posts = thread.firstChildElement("posts_count").text().toUInt();
			QString lastModified = thread.firstChildElement("last_modified").text();
			dateTimeRx.indexIn(lastModified);
			info.timestamp = QDateTime(QDate(dateTimeRx.cap(1).toUInt(),
											 dateTimeRx.cap(2).toUInt(),
											 dateTimeRx.cap(3).toUInt()),
									   QTime(dateTimeRx.cap(4).toUInt(),
											 dateTimeRx.cap(5).toUInt(),
											 dateTimeRx.cap(6).toUInt())).toTime_t();
			info.leaderPicUrl = QUrl(thread.firstChildElement("posts").firstChildElement("files").firstChildElement("thumb").text());
			list.append(info);
		}
		return list;
	}

	QList<PostInfo> HanabiraParser::getPosts(const ThreadId& threadId, const QByteArray& input)
	{
		//log(llDebug, "HanabiraParser::getPosts(): %s", input.data());
		QRegExp dateTimeRx("(\\d+)-(\\d+)-(\\d+) (\\d+):(\\d+):(\\d+)");
		QList<PostInfo> list;
		QMap<PostId, int> postPositions;
		QDomDocument doc;
		doc.setContent(input);
		QDomNodeList posts = doc.firstChildElement("result").firstChildElement("boards").firstChildElement(m_impl->nakedBoardName()).firstChildElement("threads").elementsByTagName("posts");
		log(llDebug, "HanabiraParser: %d posts", posts.count());
		for(int i = 0; i < posts.count(); i++)
		{
			QDomElement post = posts.at(i).toElement();
			PostInfo info;
			info.post_id = PostId(threadId.chanName(), threadId.boardId(), threadId.threadNumericId(), post.firstChildElement("display_id").text().toUInt());
			info.sage = post.firstChildElement("sage").text() == "True";
			info.topic = post.firstChildElement("subject").text();
			info.text = post.firstChildElement("message").text();
			info.text = m_impl->processPostText(info.text, threadId);
			info.name = post.firstChildElement("name").text();

			dateTimeRx.indexIn(post.firstChildElement("date").text());
			info.timestamp = QDateTime(QDate(dateTimeRx.cap(1).toUInt(),
											 dateTimeRx.cap(2).toUInt(),
											 dateTimeRx.cap(3).toUInt()),
									   QTime(dateTimeRx.cap(4).toUInt(),
											 dateTimeRx.cap(5).toUInt(),
											 dateTimeRx.cap(6).toUInt())).toTime_t();


			postPositions.insert(info.post_id, i);

			QDomNodeList files = post.elementsByTagName("files");
			for(int j = 0; j < files.count(); j++)
			{
				QDomElement file = files.at(j).toElement();
				AttachmentInfo attach;
				attach.thumbUrl = QUrl(file.firstChildElement("thumb").text());
				attach.url = QUrl(file.firstChildElement("src").text());
				info.attachments.append(attach);
			}
			QList<PostId> links = findLinks(threadId, info.text);
			foreach(PostId link, links)
			{
				int pos = postPositions.value(link);
				if((pos >= 0) && (pos < list.size()))
					list[pos].replies.insert(link);

			}
			list.append(info);
		}
		return list;
	}

	QString HanabiraParserImpl::processPostText(const QString& posttext, const ThreadId& threadId)
	{
		QString result = posttext;
		result.replace(QRegExp(">>(\\d+)"), QString("<a href=\"post://%1/\\1\">&gt;&gt;\\1</a>").arg(threadId.threadNumericId()));
		result.replace(m_italic, "<i>\\1</i>");
		result.replace(m_bold, "<b>\\1</b>");
		result.replace("\n", "<br />");
		return result;
	}

	PostId HanabiraParser::getPostID(const QString& link, const ThreadId& threadId)
	{
		if(link.trimmed().isEmpty())
			return PostId();
		QRegExp rx(QString("post://%1/(\\d+)").arg(threadId.threadNumericId()));
		if(rx.indexIn(link) != -1)
		{
			return PostId(threadId.chanName(), threadId.boardId(), threadId.threadNumericId(), rx.cap(1).toULong());
		}
		return PostId();
	}

	QString HanabiraParser::id()
	{
		return "hanabira";
	}

	QList<PostId> HanabiraParser::findLinks(const ThreadId& threadId, const QString& posttext)
	{
		QList<PostId> links;
		QRegExp rx("&gt;&gt;(\\d+)");
		int pos = 0;
		while((pos = rx.indexIn(posttext, pos)) != -1)
		{
			unsigned int postId = rx.cap(1).toUInt();
			if(postId != 0)
			{
				links.append(PostId(threadId.chanName(), threadId.boardId(), threadId.threadNumericId(), postId));
			}
			pos += rx.matchedLength();
		}
		return links;
	}

	BoardParser* HanabiraParser::clone(const QString& boardname)
	{
		return new HanabiraParser(boardname);
	}

	void HanabiraParser::setTimestampFormat(const QString& format)
	{
		m_impl->m_timestampParser.setPattern(format);
	}
}

Q_EXPORT_PLUGIN2(hanabiraparser, Wishmaster::HanabiraParser)

